﻿using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;

namespace Spread_Wings_Demo.Helper
{
    public class FocusIndicator : Control
    {
        private readonly Panel _container;

        public FocusIndicator(Panel container)
        {
            this.DefaultStyleKey = typeof(FocusIndicator);

            _container = container;

#if !SILVERLIGHT
            this.ClipToBounds = false;
#endif

            this.Loaded += OnLoaded;
            this.Unloaded += OnUnloaded;
        }

        public static readonly DependencyProperty FocusedElementProperty = DependencyProperty.Register(
            "FocusedElement",
            typeof(string),
            typeof(FocusIndicator),
            new PropertyMetadata(null));

        public string FocusedElement
        {
            get { return (string)GetValue(FocusedElementProperty); }
            set { SetValue(FocusedElementProperty, value); }
        }

        public static readonly DependencyProperty ElementNameVisibilityProperty = DependencyProperty.Register(
            "ElementNameVisibility", 
            typeof(Visibility),
            typeof(FocusIndicator),
            new PropertyMetadata(Visibility.Collapsed));
        public Visibility ElementNameVisibility
        {
            get { return (Visibility)GetValue(ElementNameVisibilityProperty); }
            set { SetValue(ElementNameVisibilityProperty, value); }
        }

        private Panel Container
        {
            get
            {
                return this._container;
            }
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
#if SILVERLIGHT
                Container.GotFocus += RootVisual_GotFocus;
#else
                Container.AddHandler(GotFocusEvent, new RoutedEventHandler(RootVisual_GotFocus));
#endif
        }
        void OnUnloaded(object sender, RoutedEventArgs e)
        {
#if SILVERLIGHT
            Container.GotFocus -= RootVisual_GotFocus;
#else
            Container.RemoveHandler(GotFocusEvent, new RoutedEventHandler(RootVisual_GotFocus));
#endif
        }

        void RootVisual_GotFocus(object sender, RoutedEventArgs e)
        {
#if SILVERLIGHT
            var focusedElement = FocusManager.GetFocusedElement() as FrameworkElement;
#else
            var focusedElement = Keyboard.PrimaryDevice.FocusedElement as FrameworkElement;
#endif
            if (focusedElement == null)
            {
                var element = "(None)";
                this.FocusedElement = element;
                Debug.WriteLine(element);
            }
            else
            {
                var element = string.Format("{0}:{1}", focusedElement.GetType().Name, focusedElement.Name);
                this.FocusedElement = element;
                Debug.WriteLine(element);
            }
        }
    }

    public class FocusDecorator : FrameworkElement
    {
        public FocusDecorator()
        {
            this.Loaded += OnLoaded;
            this.Unloaded += OnUnloaded;
        }

        private bool _isLoaded;
        private Popup _popup;
        private FocusIndicator _indicator;
        public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register(
            "IsEnabled",
            typeof(bool),
            typeof(FocusDecorator),
            new PropertyMetadata(true, OnIsEnabledChangedThunk));
        public bool IsEnabled
        {
            get { return (bool)GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }
        private Panel Container
        {
            get
            {
                return this.Parent as Panel;
            }
        }

        public static readonly DependencyProperty ElementNameVisibilityProperty = DependencyProperty.Register(
            "ElementNameVisibility",
            typeof(Visibility),
            typeof(FocusDecorator),
            new PropertyMetadata(Visibility.Collapsed));
        public Visibility ElementNameVisibility
        {
            get { return (Visibility)GetValue(ElementNameVisibilityProperty); }
            set { SetValue(ElementNameVisibilityProperty, value); }
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
            this._isLoaded = true;

            _popup = new Popup();
            _popup.IsHitTestVisible = false;
#if !SILVERLIGHT
            _popup.AllowsTransparency = true;
            _popup.PlacementTarget = this.Container;
            _popup.Placement = PlacementMode.Relative;
            _popup.ClipToBounds = false;
#endif

            Border content = new Border();
            _popup.Child = content;

            _indicator = new FocusIndicator(this.Container);
            _indicator.SetBinding(FocusIndicator.ElementNameVisibilityProperty,
                                  new Binding("ElementNameVisibility") { Source = this });
            content.Child = _indicator;

#if SILVERLIGHT
            Container.GotFocus -= RootVisual_GotFocus;
#else
            Container.RemoveHandler(GotFocusEvent, new RoutedEventHandler(RootVisual_GotFocus));
#endif
            if (this.IsEnabled)
            {
#if SILVERLIGHT
                Container.GotFocus += RootVisual_GotFocus;
#else
                Container.AddHandler(GotFocusEvent, new RoutedEventHandler(RootVisual_GotFocus));
#endif
            }

            Container.Children.Add(_popup);

            this.UpdateIndicator();
        }
        void OnUnloaded(object sender, RoutedEventArgs e)
        {
            this._isLoaded = false;
#if SILVERLIGHT
            Container.GotFocus -= RootVisual_GotFocus;
#else
            Container.RemoveHandler(GotFocusEvent, new RoutedEventHandler(RootVisual_GotFocus));
#endif
            this.UpdateIndicator();
        }
        private static void OnIsEnabledChangedThunk(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as FocusDecorator).OnIsEnabledChanged(e);
        }
        private void OnIsEnabledChanged(DependencyPropertyChangedEventArgs e)
        {
            if (!_isLoaded)
            {
                return;
            }

#if SILVERLIGHT
            Container.GotFocus -= RootVisual_GotFocus;
#else
            Container.RemoveHandler(GotFocusEvent, new RoutedEventHandler(RootVisual_GotFocus));
#endif
            if (this.IsEnabled)
            {
#if SILVERLIGHT
                Container.GotFocus += RootVisual_GotFocus;
#else
                Container.AddHandler(GotFocusEvent, new RoutedEventHandler(RootVisual_GotFocus));
#endif
            }

            UpdateIndicator();
        }
        private void RootVisual_GotFocus(object sender, RoutedEventArgs e)
        {
            UpdateIndicator();
        }
        private void UpdateIndicator()
        {
            if (!this._isLoaded || !this.IsEnabled)
            {
                this._popup.IsOpen = false;
                return;
            }

#if SILVERLIGHT
            UIElement focusedElement = FocusManager.GetFocusedElement() as UIElement;
#else
            UIElement focusedElement = Keyboard.PrimaryDevice.FocusedElement as UIElement;
#endif
            if (focusedElement == null || !focusedElement.IsDescendantOf(Container))
            {
                this._popup.IsOpen = false;
                return;
            }

            Rect bounds = new Rect(0, 0, focusedElement.RenderSize.Width, focusedElement.RenderSize.Height);
            bounds = focusedElement.TransformToVisual(Container).TransformBounds(bounds);

            this._popup.HorizontalOffset = bounds.Left - this._indicator.Margin.Left;
            this._popup.VerticalOffset = bounds.Top - this._indicator.Margin.Top;
            this._indicator.Width = bounds.Width;
            this._indicator.Height = bounds.Height;

            this._popup.IsOpen = true;
        }
    }

#if SILVERLIGHT
    internal static class UIElementExtensions
    {
        public static bool IsDescendantOf(this UIElement element, UIElement container)
        {
            return true;
        }
    }
#endif
}
